package ink.xiaobaibai.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import ink.xiaobaibai.common.DeviceCommon;
import ink.xiaobaibai.entity.OxygenDevice;
import ink.xiaobaibai.entity.OzoneDevice;
import ink.xiaobaibai.enumOxy.DeviceStatus;
import ink.xiaobaibai.service.IJumpService;
import ink.xiaobaibai.service.IOxygenDeviceService;
import ink.xiaobaibai.service.IOzoneDeviceService;
import ink.xiaobaibai.serviceOxy.onePay.IDelayCloseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @description: 心跳控制层
 * @author: 小白白
 * @create: 2021-06-10
 **/

@Service
public class JumpServiceImpl implements IJumpService {

    @Autowired
    private IOzoneDeviceService ozoneDeviceService;

    @Autowired
    private IOxygenDeviceService oxygenDeviceService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private IDelayCloseService delayCloseService;

//    @Autowired
//    private IDeviceOnlineTimeService onlineTimeService;

    /**
     * 监听定点的 制氧机+臭氧机
     * 定点: 监视规则
     * xx时间内,必须收到定点机器的心跳,如果没有收到,将让他下线;
     * 当前机器: 定点机器,每个 时间段 就使用redis的set装入所有的定点机器码,然后在这个时间段内,接收机器码,最后求差集
     */

    /**
     * 所有set的key
     */
    public static String allKey = "one-pay-all-jump";
    /**
     * 应答set的key
     */
    public static String answerKey = "one-pay-answer-jump";
    /**
     * 时间段 (秒)
     */
    public static int timeSlot = 600;
    /**
     * 是否统计中
     */
    private volatile boolean isRecording = false;
    /**
     * 临时set
     */
    private Set<String> tempSet = new HashSet<>();
    /**
     * 记录下线的set
     */
    private Set<String> offLineSet = new HashSet<>();

    /**
     * 监听初始化
     * 收集定点的机器码
     * 空闲状态,运行状态,断线状态 除(停运状态+故障状态)之外,收集它们的机器码
     * 3小时收集一次
     */
    private void initAllDevice() {
        //臭氧机机器码
        QueryWrapper<OzoneDevice> q1 = new QueryWrapper<>();
        q1.select("device_code");
        q1.in("status", DeviceStatus.INUSE.getStatus(), DeviceStatus.FREE.getStatus(), DeviceStatus.OFFLINE.getStatus());
        List<String> ozoneIds = this.ozoneDeviceService.list(q1).stream().map(OzoneDevice::getDeviceCode).collect(Collectors.toList());
        String[] codeArr1 = new String[ozoneIds.size()];
        ozoneIds.toArray(codeArr1);
        //氧气机器码
        QueryWrapper<OxygenDevice> q2 = new QueryWrapper<>();
        q2.select("device_code");
        q2.in("status", DeviceStatus.INUSE.getStatus(), DeviceStatus.OFFLINE.getStatus());
        List<String> oxygenIds = this.oxygenDeviceService.list(q2).stream().map(OxygenDevice::getDeviceCode).collect(Collectors.toList());
        String[] codeArr2 = new String[oxygenIds.size()];
        oxygenIds.toArray(codeArr2);
        //都放入redis集合中
        this.stringRedisTemplate.opsForSet().remove(allKey);
        this.stringRedisTemplate.opsForSet().add(allKey, codeArr1);
        this.stringRedisTemplate.opsForSet().add(allKey, codeArr2);
    }

    /**
     * 开始统计
     */
    public void statisticsDevice() {
        this.isRecording = true;
        //获取差集(未应答的机器)
        Set<String> difference = this.stringRedisTemplate.opsForSet().difference(allKey, answerKey);
        //清除应答
        this.stringRedisTemplate.opsForSet().remove(answerKey);
        this.isRecording = false;
        difference.removeAll(this.tempSet);
        this.tempSet.clear();

        Set<Integer> oxygenIds = new HashSet<>();
        //处理断线的机器,数据库中改变它们的状态
        Iterator<String> iterator = difference.iterator();
        while (iterator.hasNext()) {
            String deviceCode = iterator.next();
            if (deviceCode.startsWith(DeviceCommon.ONE_PAY_OXYGEN_PREFIX)) {
                //放入坏的氧气机
                QueryWrapper<OxygenDevice> q1 = new QueryWrapper<>();
                q1.select("id");
                q1.eq("device_code", deviceCode);
                OxygenDevice oxygenDevice = this.oxygenDeviceService.getOne(q1);
                if (oxygenDevice != null) {
                    oxygenIds.add(oxygenDevice.getId());
                }
                //iterator.remove();//后续重新验证,所以这里不可去除
            }
        }
        //臭氧机掉线了
        UpdateWrapper<OzoneDevice> u1 = new UpdateWrapper<>();
        u1.set("status", DeviceStatus.OFFLINE.getStatus());
        u1.in("device_code", difference);
        this.ozoneDeviceService.update(u1);

        //再次从redis中拿出检验一下,最后记录掉线的deviceCode
        Set<String> answerKey = this.stringRedisTemplate.opsForSet().members(JumpServiceImpl.answerKey);
        List<String> ozoneOnLineCodes = new ArrayList<>();
        Iterator<String> iterator2 = difference.iterator();
        while (iterator2.hasNext()) {
            String deviceCode = iterator2.next();
            if (answerKey.contains(deviceCode)) {
                //又重新上线了
                if (deviceCode.startsWith(DeviceCommon.ONE_PAY_OZONE_PREFIX)) {
                    //臭氧机
                    /**
                     * 注意这里的下线状态恢复可能会与转为空闲状态的代码进行冲突
                     * 即掉线过程中,机器可能是运行状态的,下线状态恢复时,就去延时队列的set验证一下即可
                     */
                    ozoneOnLineCodes.add(deviceCode);
                } else {
                    //氧气机,氧气机最后
                    QueryWrapper<OxygenDevice> q1 = new QueryWrapper<>();
                    q1.select("id");
                    q1.eq("device_code", deviceCode);
                    OxygenDevice oxygenDevice = this.oxygenDeviceService.getOne(q1);
                    if (oxygenDevice != null) {
                        //移除坏的氧气机
                        oxygenIds.remove(oxygenDevice.getId());
                    }
                }
                iterator.remove();
            }
        }
        //重新上线的臭氧机
        for (String deviceCode : ozoneOnLineCodes) {
            //重新上线
            this.onLineOzone(deviceCode);
        }
        //下线的氧气机,对应氧气机掉线了,则旗下所有臭氧都置为下线,氧气机应该最后来搞定
        //氧气机无需转为掉线状态,对于用户来说,只需判断臭氧机就行了
        UpdateWrapper<OzoneDevice> u2 = new UpdateWrapper<>();
        u2.set("status", DeviceStatus.OFFLINE.getStatus());
        u2.in("oxygen_id", oxygenIds);
        this.ozoneDeviceService.update(u2);
        //记录下线的set
        this.offLineSet = new HashSet<>(difference);
    }

    /**
     * 传进活跃的机器码
     */
    @Override
    public void jump(String deviceCode) {
        if (this.isRecording) {
            //正在统计中,那么就放入jvm的set
            this.tempSet.add(deviceCode);
        } else {
            this.stringRedisTemplate.opsForSet().add(answerKey, deviceCode);
            if (this.offLineSet.contains(deviceCode)) {
                //立即对其上线
                this.offLineSet.remove(deviceCode);
                this.onLineOzone(deviceCode);
            }
        }
        //记录上线日志 (启动此log)
//        onlineTimeService.save(new DeviceOnlineTime(deviceCode));
    }

    /**
     * 上线臭氧机
     */
    private void onLineOzone(String deviceCode) {
        QueryWrapper<OzoneDevice> q1 = new QueryWrapper<>();
        q1.select("id", "work_stop_time");
        q1.eq("device_code", deviceCode);
        OzoneDevice ozoneDevice = this.ozoneDeviceService.getOne(q1);
        UpdateWrapper<OzoneDevice> u1 = new UpdateWrapper<>();
        if (ozoneDevice != null) {
            u1.eq("id", ozoneDevice.getId());
        } else {
            u1.eq("device_code", deviceCode);
        }
        if (ozoneDevice != null && ozoneDevice.getWorkStopTime().getTime() > System.currentTimeMillis()) {
            //将其置为运行状态
            u1.set("status", DeviceStatus.INUSE.getStatus());
        } else {
            //将其置为空闲状态
            u1.set("status", DeviceStatus.FREE.getStatus());
        }
        this.ozoneDeviceService.update(u1);
    }

    /**
     * 执行检测
     */
    public void execute() {
        this.statisticsDevice();
    }

}
